#include "LogAppender.h"

#include <boost/thread/mutex.hpp>
#include <boost/tokenizer.hpp>

namespace jmlib {
	LogAppender::LogAppender(const std::string& format,  const std::string& conf)
		: config_(conf) {
		parseLogFormat(format);
	}

	void LogAppender::parseLogFormat(const std::string& format) {
		// source line == %s
		// function name == %f
		// logger name == %n
		// log level == %l
		// leg message == %m
		// time / time format == %t
		// "%s %f %n %m %t "
		logFragmentMessageIndex_ = 10000;
		std::string fragment;
		for (size_t i = 0; i < format.size(); ++i) {
			if (format[i] == '%') {
				if (i < format.size() - 1) {
					++i;
					LogFragmentType t = PLAIN_TEXT;
					switch (format[i]) {
					case 'l':
						t = LINE_NUMBER;
						break;
					case 'f':
						t = FUNCTION_NAME;
						break;
					case 'i':
						t = LOGGER_ID;
						break;
					case 'x':
						t = LOG_LEVEL;
						break;
					case 'm':
						logFragmentMessageIndex_ = fragment.empty() ? fragments_.size() : fragments_.size() + 1;
						t = LOG_MESSAGE;
						break;
					case 't':
						t = LOG_TIME;
						break;
					case 'a':
						t = LOGGER_ADDRESS;
						break;
					default:
						fragment += format[i];
						break;
					}
					if (t != PLAIN_TEXT) {
						if (!fragment.empty()) {
							fragments_.push_back(LogFragment(PLAIN_TEXT, fragment));
							fragment.clear();
						}
						fragments_.push_back(LogFragment(t));
					}
				}
				else {
					fragment += format[i];
				}
			}
			else {
				fragment += format[i];
			}
		}
		if (!fragment.empty()) {
			fragments_.push_back(LogFragment(PLAIN_TEXT, fragment));
			fragment.clear();
		}
	}

	void LogAppender::dumpFragments(size_t from, size_t to) {
		for (size_t i = from; i < to && i < fragments_.size(); ++i) {
			switch (fragments_[i].fragmentType_) {
			case LINE_NUMBER:
				getOutputStream() << lineNumber_;
				break;
			case FUNCTION_NAME:
				getOutputStream() << functionName_;
				break;
			case LOGGER_ID:
				getOutputStream() << loggerId_;
				break;
			case LOG_LEVEL:
				getOutputStream() << logLevel_;
				break;
			case LOG_MESSAGE:
				assert(false);
				break;
			case LOG_TIME:
				assert(false);
				//getOutputStream() << logTime_;
				break;
			case PLAIN_TEXT:
				getOutputStream() << fragments_[i].fragment_;
				break;
			case LOGGER_ADDRESS:
				getOutputStream() << "0x"<< std::hex << loggerThis_ << std::dec;
				break;
			default:
				assert(false);
			}
		}
	}


	void LogAppender::lockLog(bool lock) {
		static boost::mutex m;
		if (lock) {
			m.lock();
		}
		else {
			m.unlock();
		}

	}

	void LogAppender::beforeLogging(const std::string& loggerId, 
		const std::string& logLevel, const char* functionName, 
		size_t lineNumber, const int loggerThis) {

			loggerId_ = loggerId;
			logLevel_ = logLevel;
			functionName_ = functionName;
			lineNumber_ = lineNumber;
			loggerThis_ = loggerThis;
			dumpFragments(0, logFragmentMessageIndex_);
	}


	void LogAppender::afterLogging() {
		dumpFragments(logFragmentMessageIndex_ + 1, fragments_.size());
		getOutputStream() << std::flush;
	}

	void LogAppender::addActivationFilter(LoggerActivatorFilter* f) {
		activationFilters_.insert(f);
	}

	bool LogAppender::isLoggerAccepted(const std::string& loggerName) {
		for (auto it = activationFilters_.begin(); 
			it != activationFilters_.end(); ++it) {
				if ((*it)->isLoggerAccepted(loggerName)) {
					return true;
				}
		}
		return false;
	}

	void FileLogAppender::initConfig(const std::string& conf) {
		boost::tokenizer<boost::escaped_list_separator<char>> t(conf, 
			boost::escaped_list_separator<char>('\\', ':', '"'));
		for (auto it = t.begin(), end = t.end(); it != end ;++it) {
			if ((*it) == "file") {
				++it;
				if (it != end) {
					file_ = *it;
				}
			}
			else if ((*it) == "append") {
				++it;
				if (it != end) {
					append_ = (*it) == "true";
				}
			}

		}	
	}

	void FileLogAppender::initConfigs(const std::string& confs) {
		boost::tokenizer<boost::escaped_list_separator<char>> t(confs, 
			boost::escaped_list_separator<char>('\\', ';', '"'));
		for (auto it = t.begin(), end = t.end(); it != end ;++it) {
			initConfig(*it);	
		}	
	}

} // namespace jmlib
